#include "PolyList.hpp"
#include <iostream>
#include <cmath>
#include <time.h>

Polygon3P::Polygon3P()
    : A(QVector3D(0,0,0)),B(QVector3D(0,0,0)),C(QVector3D(0,0,0))
{}
Polygon3P::Polygon3P(QVector3D a, QVector3D b, QVector3D c)
    : A(a), B(b), C(c)
{}

GLfloat Polygon3P::getSurface() const
{
    //Formule de Héron
    GLfloat a = (B-C).length();
    GLfloat b = (A-C).length();
    GLfloat c = (A-B).length();
    GLfloat s = 0.5 * (a+b+c);
    return sqrt(s*(s-a)*(s-b)*(s-c));
}


PolyList::PolyList(float * vertices, int * indices, int nb)
{
    for(int i = 0; i< nb; ++i)
    {
        Polygon3P t(  QVector3D(vertices[indices[i*3]*3], vertices[indices[i*3]*3+1], vertices[indices[i*3]*3+2])
                      , QVector3D(vertices[indices[i*3+1]*3], vertices[indices[i*3+1]*3+1], vertices[indices[i*3+1]*3+2])
                      , QVector3D(vertices[indices[i*3+2]*3], vertices[indices[i*3+2]*3+1], vertices[indices[i*3+2]*3+2])
                      );
        polyList.push_back(t);
    }
}

PolyList::PolyList( std::vector<GLfloat> &verticesArray, std::vector<GLuint> &indicesArray )
{
    addPolygons(verticesArray, indicesArray);
}

void PolyList::addPolygons( std::vector<GLfloat> &verticesArray, std::vector<GLuint> &indicesArray )
{
	for(size_t i = 0; i < indicesArray.size(); i += 3)
    {
        Polygon3P  poly(QVector3D( verticesArray[indicesArray[i]*3    ], verticesArray[indicesArray[i    ]*3 + 1], verticesArray[indicesArray[i    ]*3 + 2] ),
                        QVector3D( verticesArray[indicesArray[i + 1]*3], verticesArray[indicesArray[i + 1]*3 + 1], verticesArray[indicesArray[i + 1]*3 + 2] ),
                        QVector3D( verticesArray[indicesArray[i + 2]*3], verticesArray[indicesArray[i + 2]*3 + 1], verticesArray[indicesArray[i + 2]*3 + 2] ));
        polyList.push_back(poly);
    }
}

const Polygon3P PolyList::getPoly(int i) const
{
    try
    {
        return polyList[i];
    }
    catch(std::exception& e)
    {
        return Polygon3P();
    }
}

GLfloat PolyList::getTotalSurface() const
{
    GLfloat sum = 0;
    for(size_t i= 0; i < polyList.size(); ++i)
    {
        sum += polyList.at(i).getSurface();
    }
    return sum;
}


QVector3D Polygon3P::baryToCart(GLfloat u, GLfloat v, Polygon3P t)
{
    QVector3D res;
    res.setX( ( (1 - u - v) * t.getA().x() ) + (u * t.getB().x()) + (v * t.getC().x()) );
    res.setY( ( (1 - u - v) * t.getA().y() ) + (u * t.getB().y()) + (v * t.getC().y()) );
    res.setZ( ( (1 - u - v) * t.getA().z() ) + (u * t.getB().z()) + (v * t.getC().z()) );
    return res;
}

std::vector<QVector3D> PolyList::jitteringPolygon3P(const Polygon3P & p, int nbPoint)
{
    std::vector<QVector3D> self;
    int number = (int)sqrt(nbPoint);
    int excess = nbPoint - number * number;
    float proba = (float)excess / (number*number);

    QVector3D vec1 = (p.getB() - p.getA())/number;
    QVector3D vec2 = (p.getC() - p.getA())/number;

    for(int i = 0; i < number; i++)
    {
        for(int j = 0; j < number-i; j++)
        {
            QVector3D v1 = p.getA() + i * vec1 + j * vec2;
            QVector3D v2 = v1 + vec1;
            QVector3D v3 = v1 + vec2;
            QVector3D v4 = v2 + vec2;
            self.push_back( polygonRandomPoint(v1, v2, v3) );
            if( percentRandom() < proba && excess > 0){
                self.push_back( polygonRandomPoint(v1, v2, v3) );
                excess--;
            }

            if( i+j < (number - 1) )
            {
                self.push_back( polygonRandomPoint(v2, v3, v4) );
                if( percentRandom() < proba && excess > 0){
                    self.push_back( polygonRandomPoint(v2, v3, v4) );
                    excess--;
                }
            }
        }
    }
    while(excess > 0)
    {
        self.push_back( polygonRandomPoint(p.getA(), p.getB(), p.getC()));
        excess--;
    }
    return self;
}

std::vector<QVector3D> PolyList::sceneSampling(PolyList p, int nbOfPoints)
{
    std::vector<QVector3D> res;
    srand(time(NULL));
    if(p.getListSize() == 0 )
    {
        return res;
    }
    GLfloat surfaceTotale = p.getTotalSurface();

    for (int i=0; i < p.getListSize(); ++i )
    {
        GLfloat tmp = p.getPoly(i).getSurface()/surfaceTotale;
        int nb = floor(nbOfPoints * tmp);
        std::vector<QVector3D> points = jitteringPolygon3P(p.getPoly(i), nb);
        for(size_t j = 0; j < points.size(); j++)
            res.push_back(points[j]);
    }
    return res;
}
void PolyList::addPolygons(float * vertices, int * indices, int nb)
{
    for(int i = 0; i< nb; ++i)
    {
        Polygon3P poly(  QVector3D(vertices[indices[i*3]*3], vertices[indices[i*3]*3+1], vertices[indices[i*3]*3+2])
                      , QVector3D(vertices[indices[i*3+1]*3], vertices[indices[i*3+1]*3+1], vertices[indices[i*3+1]*3+2])
                      , QVector3D(vertices[indices[i*3+2]*3], vertices[indices[i*3+2]*3+1], vertices[indices[i*3+2]*3+2])
                      );
        polyList.push_back(poly);
    }
}
